<?php

    namespace yaliSDK\db;

    use function app\oa\model\powerRecursion;
    use \think\cache\driver\Redis;

    /**
     * Created by PhpStorm.
     * User: fang
     * Date: 2018/1/9
     * Time: 上午9:32
     */
    class RedisDB extends Redis implements IDB {

        // 队尾弹出
        public function rPop($name) {
            $key = $this->getCacheKey($name);
            $result = $this->handler->rPop($key);
            return $result;
        }


        public function lPop($name) {
            $key = $this->getCacheKey($name);
            $result = $this->handler->lPop($key);
            return $result;
        }

        /**
         * @param $name
         * @param $value
         * @return int
         * 删除list元素
         */
        public function lRem($name, $value) {
            if ($this->tag && !$this->has($name)) {
                $first = true;
            }
            $key = $this->getCacheKey($name);
            $result = $this->handler->lRem($key, $value);
            isset($first) && $this->setTagItem($key);
            return $result;
        }

        /**
         * @param string $table
         * @param $data (string or array)
         * @return TPDBResult
         * 默认写入集合
         */
        public function insert(DBQuery $query): IDBResult {
            $table = $query->getTable();
            $data = $query->getData();
            $field = $query->getField();
            $handleType = $query->getHandleType();// 判断是用lpush，还是set
            switch ($handleType) {
                case 'lpush':
                    $return_data = $this->handler->lPush($table . $field, self::check_scalar($data));
                    break;
                case 'lpushAll':
                    $return_data = $this->handler->lPush($table . $field, ...$data);
                    break;
                case 'rpush':
                    $return_data = $this->handler->rPush($table . $field, self::check_scalar($data));
                    break;
                case 'rpushAll':
                    $return_data = $this->handler->rPush($table . $field, ...$data);
                    break;
                case 'setnx':
                    $expire = 0;
                    if ($query->getExpire()) {
                        $expire = $query->getExpire() + time();
                    }
                    $return_data = $this->handler()->setnx($table . $field, $expire);
                    break;
                default:
                    $return_data = $this->set($table . $field, $data);
                    break;
            }
            return new RedisDBResult($return_data);
        }

        public function update(DBQuery $query): IDBResult {
            return $this->insert($query);
        }

        public function delete(DBQuery $query): IDBResult {
            $table = $query->getTable();
            $field = $query->getField();
            $handleType = $query->getHandleType();
            switch ($handleType) {
                case 'lpop':
                    $return_data = $this->lPop($table . $field);// 从表队头弹出一个
                    break;
                case 'rpop':
                    $return_data = $this->rPop($table . $field);// 从表队尾弹出一个
                    break;
                case 'lrem':
                    $return_data = $this->lRem($table, $field);
                    break;
                default:
                    $return_data = $this->rm($table . $field, '');
                    break;
            }
            return new RedisDBResult($return_data);
        }

        public function find(DBQuery $query): IDBResult {
            $table = $query->getTable();
            $field = $query->getField();
            $handleType = $query->getHandleType();
            $return_data = [];
            switch ($handleType) {
                case 'llen':
                    $return_data = $this->handler->lLen($table . $field);
                    break;
                case 'lrange':
                    $return_data = $this->handler()->lRange($table . $field, $query->getStart(), $query->getStop());
                    if (is_array($return_data)) {
                        foreach ($return_data as $key => $value) {
                            $return_data[$key] = self::unserialize_val($value);
                        }
                    }
                    break;
                case 'keys':
                    $field = !empty($field) ? $field : '*';
                    $return_data = $this->handler()->keys($table . $field);
                    break;
                case 'scan':
                    $field = $table . (!empty($field) ? $field : '*');
                    $iterator = null;
                    while (($iterator > 0 || $iterator === null)) {
                        $keys = $this->handler()->scan($iterator, $field);
                        $return_data = array_merge($keys, $return_data);
                    };
                    break;
                default:
                    $return_data = $this->get($table.$field);
                    break;
            }
            return new RedisDBResult($return_data);
        }

        public function count(DBQuery $query): IDBResult {
            // TODO: Implement count() method.
        }

        public function list(DBQuery $query): IDBResult {
            // TODO: Implement list() method.
        }

        public function flushall(DBQuery $query): IDBResult {
            $return_data = $this->handler()->flushall();
            return new RedisDBResult($return_data);
        }

        /**
         * @param array $data
         * @return bool|float|int|string
         * 检测变量是否是一个标量
         * 标量变量是指那些包含了 integer、float、string 或 boolean的变量，而 array、object 和 resource 则不是标量
         */
        private static function check_scalar($data = []) {
            return is_scalar($data) ? $data : 'think_serialize:' . serialize($data);
        }

        /**
         * @param $value
         * @return mixed
         * 反序列化
         */
        private static function unserialize_val($value) {
            return 0 === strpos($value, 'think_serialize:') ? unserialize(substr($value, 16)) : $value;
        }
        public function getHandler() {
            return $this->handler;
        }
    }